<template>
  <div class="card">
    <div class="card-body" style="min-height: 86vh">
      <div class="d-flex justify-content-between">
        <h4 class="address-title">地址别名管理</h4>
        <el-checkbox v-model="isAllAddress" @change="handleAllChange">全国</el-checkbox>
      </div>
      <div class="address-thead">
        <div class="flex pl-3">地址</div>
        <div class="address-tag-w text-right pr-2">别名</div>
        <div class="address-edit-w">操作</div>
      </div>
      <el-tree
        :props="props"
        :load="loadNode"
        v-loading="loading"
        lazy
        class="address-tree"
        ref="tree"
      >
        <div class="address-tree-node" slot-scope="{ node, data }">
          <div class="flex">{{ node.label }}</div>
          <div class="d-flex justify-content-end flex-wrap address-tag-w">
            <el-tag
              v-for="item in data.alias"
              :key="item.id"
              :closable="item.closable"
              :type="item.closable ? '' : 'info'"
              size="small"
              class="address-tag"
              v-loading="item.loading"
              element-loading-spinner="el-icon-loading"
              @click.stop.native
              @close.stop="() => handleTagClose(item, node, data)"
            >
              {{ item.alias_name }}
            </el-tag>
          </div>
          <div style="margin: 1px" class="d-flex address-edit-w">
            <div v-show="data.edit">
              <div class="d-flex">
                <el-input
                  v-model="data.editValue"
                  placeholder="填写"
                  class="address-tag-input"
                  size="mini"
                  @click.stop.native
                  @focus.stop
                  @keyup.enter.native="() => handleAliasSubmit(node, data)"
                ></el-input>
                <el-button
                  type="text"
                  size="mini"
                  class="ml-1"
                  :loading="data.loading"
                  @click.stop="() => handleAliasSubmit(node, data)"
                >
                  确认
                </el-button>
                <el-button
                  type="text"
                  size="mini"
                  class="ml-1"
                  :disabled="data.loading"
                  @click.stop="() => handleAliasDismiss(node, data)"
                >
                  取消
                </el-button>
              </div>
            </div>
            <el-button
              v-show="!data.edit"
              type="text"
              size="mini"
              @click.stop="e => handleAddAlias(e, node, data)"
            >
              添加别名
            </el-button>
          </div>
        </div>
      </el-tree>
    </div>
  </div>
</template>

<script>
import { mapGetters } from 'vuex'
import { addressLevel, dateFormat } from '@vimi/utils-tool'
import { getAddressNextValue } from '@/common/api/address'

export default {
  components: {},
  props: {},
  data() {
    return {
      isAllAddress: false,

      props: {
        label: 'geoName',
        isLeaf: 'leaf',
      },

      loading: false,
      tableName: 'geo_alias_list',
    }
  },
  computed: {
    ...mapGetters(['user']),
    provinceAddress() {
      const { addressCode } = this.user
      return addressCode ? addressCode.substring(0, 2).padEnd(12, '0') : ''
    },
    topValue() {
      return this.isAllAddress ? '' : this.provinceAddress
    },
    topLevel() {
      const { topValue } = this
      return topValue ? addressLevel(topValue) + 1 : 0
    },
  },
  watch: {},
  created() {},
  mounted() {},
  methods: {
    async loadNode(node, resolve) {
      const { level, data: { id = '' } = {} } = node
      const res = await this.getAddressFromNodeLevel(id, level)
      resolve(res)
    },

    async getAddressFromNodeLevel(address, nodeLevel) {
      const isRoot = nodeLevel === 0

      const addrValue = isRoot ? this.topValue : address
      const addrLevel = nodeLevel + this.topLevel
      const res = await this.getAddress(addrValue, addrLevel)

      const leaf = addrLevel > 3
      const alias = await this.getAlaisFromLevel(addrValue, addrLevel)

      return res.map(it => ({
        ...it,
        alias: alias[it.id] || [],
        edit: false,
        editValue: '',
        loading: false,
        leaf,
      }))
    },

    // 改变顶级数据时，刷新整个tree
    async refreshTree() {
      const { tree } = this.$refs
      if (!tree) return

      try {
        this.loading = true
        const res = await this.getAddressFromNodeLevel(this.topValue, 0)
        tree.store.setData(res)
      } catch (error) {
        throw new Error(error)
      } finally {
        this.loading = false
      }
    },

    // 获取地址api，用store缓存功能
    getAddress(address = '', level = 0) {
      return this.$store.dispatch('getAddress', { address, level })
    },

    // 获取地址下一级所有的别名
    getAlaisFromLevel(address, level) {
      const nextLevel = level + 1
      const params = {
        'region_lever.eq': nextLevel,
        fields: 'id,geo_id,geo_name,alias_name,region_lever',
      }
      if (address) {
        params['geo_id.gt'] = address
        params['geo_id.lt'] = getAddressNextValue(address)
      }
      return this.getAlias(params).then(res =>
        res.reduce((prev, curr) => {
          curr = this.aliasAdapter(curr)
          const id = curr.geo_id
          ;(prev[id] || (prev[id] = [])).push(curr)
          return prev
        }, {})
      )
    },

    aliasAdapter(item) {
      return {
        ...item,
        closable: item.alias_name !== item.geo_name,
        loading: false,
      }
    },

    // 获取当前地址的别名
    getAliasFromAddress(address) {
      return this.getAlias({
        'geo_id.eq': address,
      })
    },

    getAlias(params) {
      return this.$api
        .getData(
          this.$serviceName,
          this.tableName,
          params,
          {
            create_time: 'asc',
          }
          // ).then(res => (res?.data ?? []).filter(it => it.alias_name !== it.geo_name))
        )
        .then(res => res?.data ?? [])
    },

    // 添加别名
    handleAddAlias(e, node, data) {
      data.edit = true

      // input获取焦点 span < button < div
      const inputEl = e.target.parentNode.parentNode.querySelector('input')
      if (inputEl) {
        this.$nextTick(() => {
          inputEl.focus()
        })
      }
    },

    // 确认添加别名
    handleAliasSubmit(node, data) {
      const { editValue } = data
      // 判空
      if (!editValue) {
        return this.$message.error('请输入别名')
      }

      // 当前地址，别名唯一
      const { alias: currAlias = [] } = data
      if (currAlias.find(it => it.alias_name === editValue)) {
        return this.$message.error('别名已经存在')
      }

      // 同一上级，别名唯一
      const siblingNodes = node.parent?.childNodes ?? []
      for (const snode of siblingNodes) {
        const { alias: siblingAlias = [], geoName } = snode.data
        if (siblingAlias.find(it => it.alias_name === editValue)) {
          return this.$message.error(`同级别的地址中（${geoName}）已经存在该别名`)
        }
      }

      data.loading = true
      this.addTag(data, editValue)
        .then(res => {
          this.refreshOnSuccess(node, data)
          this.$message.success('添加别名成功')
          this.$emit('success', { data, res })
        })
        .catch(err => {
          this.$message.error('添加别名失败，请重试')
          throw new Error(err)
        })
        .finally(() => {
          data.loading = false
        })
    },

    refreshOnSuccess(node, data) {
      node.loading = true
      this.getAliasFromAddress(data.id)
        .then(res => {
          data.alias = res.map(this.aliasAdapter)
          this.handleAliasDismiss(node, data)
        })
        .finally(() => {
          node.loading = false
        })
    },

    // 取消添加别名
    handleAliasDismiss(node, data) {
      data.edit = false
      data.editValue = ''
    },

    // 别名删除事件
    handleTagClose(item, node, data) {
      item.loading = true
      this.deleteTag(item.id)
        .then(() => {
          this.$message.success('删除成功')
          // 前端刷新
          const index = data.alias.findIndex(it => it.id === item.id)
          data.alias.splice(index, 1)
        })
        .catch(err => {
          this.$message.error('删除失败，请重试')
          throw new Error(err)
        })
        .finally(() => {
          item.loading = false
        })
    },

    addTag(addr, aliasName) {
      return this.$api.addData(this.$serviceName, this.tableName, {
        alias_name: aliasName,
        geo_id: addr.id,
        geo_name: addr.geoName,
        region_lever: addr.regionLever,
        update_time: dateFormat(new Date()), // 不传报错
      })
    },

    deleteTag(id) {
      return this.$api.deleteData(this.$serviceName, this.tableName, id)
    },

    handleAllChange() {
      this.refreshTree()
    },
  },
}
</script>

<style lang="scss" scoped>
.address-title {
  font-size: 1.15rem;
  font-weight: bold;
}
.address-thead {
  display: flex;
  font-weight: bold;
  padding: 0.5rem 0;
  background: $gray-100;
  border-bottom: 1px solid $gray-300;
  box-shadow: 1px 2px 4px rgba(128, 128, 128, 0.07);
  margin-bottom: 0.5rem;
}

.address-tree /deep/ .el-tree-node__content {
  height: auto;
}
.address-tree-node {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 0.875rem;
  padding: 1px 4px 1px 0;
}
.address-tag {
  height: 28px;
  line-height: 24px;
  margin: 1px 1px;
}
.address-tag-input /deep/ .el-input__inner {
  flex: 1;
  padding: 0 4px;
}

.address-tag-w {
  width: 70%;
}
.address-edit-w {
  width: 130px;
}
</style>
